#ifndef _SELECT_SERVER_H_
#define _SELECT_SERVER_H_

#include "MySock.hpp"
#include "poll.h"
#include <unistd.h>
#include <unordered_map>

const int FD_NONE = -1;  // 设定fd的初始值为-1，表示此处fd空缺，可以填入或者跳过

class PollServer
{
public:
    PollServer(const uint16_t port = 8080, int nfds = 1024):_nfds(nfds)
    {
        _listenSock = QiHai::Sock::socket();
        QiHai::Sock::bind(_listenSock, port);  // 服务器默认绑定0.0.0.0 ip
        QiHai::Sock::listen(_listenSock);

        _pollFds = new pollfd[_nfds];
        for (int i = 0; i < _nfds; ++i)
        {
            _pollFds[i].fd = FD_NONE;
            _pollFds[i].events = _pollFds[i].revents = 0;  // 置空
        }
        _pollFds[0].fd = _listenSock;
        _pollFds[0].events = POLLIN;  // 只读事件
    }

    void Start()
    {
        // 首先，将通过监听套接字获取链接视为一种IO资源，我们是读取其中的链接的
        while(true)
        {
            int n = poll(_pollFds, _nfds, -1); // 最后一个参数-1为阻塞的进行读取哦
            switch(n)
            {
            case 0:
                // 表示当前链接的所有fd没有就绪的 - 还需要进行等待 但是阻塞等待的话就不存在了
                logMessage(DEBUG, "select timeout......");
                break;
            case -1:
                logMessage(WARNING, "select error:%d-%s", errno, strerror(errno));
                break;
            default:
                // 出现文件就绪成功 IO = 等待 + 拷贝，等待成功！
                HandlerEvent();                
                break;
            }
        }
    }

    ~PollServer()
    {
        if (_listenSock >= 0) close(_listenSock);
        delete[] _pollFds;
    }
private:
    // 此时select负责的多个fd中存在就绪的了，需要对其进行处理
    void HandlerEvent()
    {
        //明确rfds是一个输入输出型参数，此时是输出型参数，如果对应fd等待资源就绪，内核会进行一个设置
        for (int i = 0; i < _nfds; ++i)
        {
            if (_pollFds[i].fd == FD_NONE) continue;
            if (_pollFds[i].revents & POLLIN)  // 表示此时对应的就绪了
            {
                if (_pollFds[i].fd == _listenSock) Accepter();  // 表示连接服务
                else Recver(i); // 正常的读服务
            }
        }
    }

    void Accepter()
    {
        // 连接服务
        int sock = QiHai::Sock::accept(_listenSock);  // 此时是就绪的，不用阻塞进行等待了 debug模式里面会打印客户端ip和port
        int pos = 1;  //从1开始
        for (; pos < _nfds; ++pos)
        {
            if (_pollFds[pos].fd == FD_NONE) break;
        }

        if (pos == _nfds)
        {
            // 此时select托管的fd满了，不好意思，只能抛弃此连接了
            close(sock);
            logMessage(WARNING, "accept error,_fdsArray overload......");
        }
        else{
            _pollFds[pos].fd = sock;
            _pollFds[pos].events = POLLIN;
        }
    }

    void Recver(int pos)
    {
        // 读取服务
        // 当前应该配合协议进行读取，但是当前为了实现简单没有对数据进行处理
        char buffer[1024];
        int n = recv(_pollFds[pos].fd, buffer, sizeof(buffer) - 1, 0);  // 应该也是无需阻塞等待，直接读取
        if (n > 0)
        {
            buffer[n] = '\0';
            logMessage(DEBUG, "fds[%d]# %s", pos, buffer);
        }
        else if (n == 0){
            // 对方关闭连接
            close(_pollFds[pos].fd);
            _pollFds[pos].fd = FD_NONE;
            logMessage(WARNING, "fds[%d] close, me too ......", pos);
        }
        else{
            // 读取出现错误
            logMessage(ERROR, "recv error %d-%s", errno, strerror(errno));
            close(_pollFds[pos].fd);
            _pollFds[pos].fd = FD_NONE;
        }
    }
private:
    int _listenSock;
    int _nfds;
    struct pollfd* _pollFds;
};



#endif